package com.xyh.transaction.service.imp;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.xyh.transaction.entity.Menu;
import com.xyh.transaction.entity.Permission;
import com.xyh.transaction.entity.Role;
import com.xyh.transaction.entity.User;
import com.xyh.transaction.entity.dto.role.RoleEditOrAddDto;
import com.xyh.transaction.entity.dto.role.RolePageDto;
import com.xyh.transaction.entity.dto.role.RoleSearchDto;
import com.xyh.transaction.entity.dto.user.ArrDto;
import com.xyh.transaction.entity.dto.user.ChangeDto;
import com.xyh.transaction.entity.vo.MenuOptionsVO;
import com.xyh.transaction.entity.vo.MetaPropsVO;
import com.xyh.transaction.entity.vo.RoleStatusVo;
import com.xyh.transaction.entity.vo.RoleVo;
import com.xyh.transaction.exception.DeleteException;
import com.xyh.transaction.exception.InsertException;
import com.xyh.transaction.exception.UpdateException;
import com.xyh.transaction.mapper.RoleMapper;
import com.xyh.transaction.mapper.MenuMapper;
import com.xyh.transaction.mapper.UserMapper;
import com.xyh.transaction.service.RoleService;
import com.xyh.transaction.utils.MappingUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

@Service
@Transactional
public class RoleServiceImpl implements RoleService {

    @Autowired
    private RoleMapper roleMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private MenuMapper menuMapper;

    @Override
    public PageInfo<RoleVo> getRoleList(RolePageDto rolePageDto) {

        Integer pageNum = rolePageDto.getPageNum();
        Integer pageSize = rolePageDto.getPageSize();

        if (pageNum != null && pageSize != null){
            PageHelper.startPage(pageNum, pageSize);
        }

        RoleSearchDto roleSearchDto = MappingUtil.mapObject(rolePageDto, RoleSearchDto.class);

        List<Role> roleList = roleMapper.findAll(roleSearchDto);

        PageInfo pageInfo = PageInfo.of(roleList);

        List<RoleVo> roleVoList = roleList.stream().map(role -> {
            RoleVo roleVo = MappingUtil.mapObject(role, RoleVo.class);
            LinkedHashSet<MenuOptionsVO> authMenuList = new LinkedHashSet<>();
            Integer id = roleVo.getId();
            List<Integer> routeIdList = roleMapper.findMenuIdListByRoleId(id);
            for (Integer menuId : routeIdList) {
                List<Menu> routeList = menuMapper.findAllByMenuId(menuId);
                for (Menu frontRoute : routeList) {
                    MenuOptionsVO menuOptionsVO = buildMenuOptions(frontRoute);
                    authMenuList.add(menuOptionsVO);
                }
            }
            Set<Permission> permissions = roleMapper.findPermissionById(id);
            roleVo.setPermissions(permissions.stream()
                    .map(Permission::getId).collect(Collectors.toSet()));
            roleVo.setRoutes(authMenuList.stream()
                    .map(MenuOptionsVO::getId)
                    .collect(Collectors.toSet()));
            return roleVo;
        }).collect(Collectors.toList());

        pageInfo.setList(roleVoList);
        return pageInfo;
    }

    private MenuOptionsVO buildMenuOptions(Menu menu) {
        MenuOptionsVO menuOptionsVO = MappingUtil.mapObject(menu, MenuOptionsVO.class);
        MetaPropsVO metaPropsVO = MappingUtil.mapObject(menu, MetaPropsVO.class);
        menuOptionsVO.setMeta(metaPropsVO);

        List<Menu> children = menuMapper.findChildrenByParentId(menu.getId());
        if (!children.isEmpty()) {
            Set<MenuOptionsVO> childrenList = new HashSet<>();
            for (Menu child : children) {
                MenuOptionsVO childMenuOptions = buildMenuOptions(child);
                childrenList.add(childMenuOptions);
            }
            menuOptionsVO.setChildren(childrenList);
        }

        return menuOptionsVO;
    }

    @Override
    public Set<RoleStatusVo> getRoleStatusList() {
        List<Role> roleList = roleMapper.findAll(new RoleSearchDto());

        return roleList.stream().map(role -> {
            RoleStatusVo statusVo = new RoleStatusVo();
            Integer status = role.getStatus();
            statusVo.setRoleStatus(status);
            statusVo.setRoleLabel(status == 0 ? "禁用" : "启用");
            return statusVo;
        }).collect(Collectors.toSet());
    }

    @Override
    public void changeStatus(ChangeDto changeDto) {
        Role role = MappingUtil.mapObject(changeDto, Role.class);
        // TODO 校验当前角色是否关联其他用户，如果有修改失败
        if (hasRoleAssociation(new Integer[]{role.getId()})) {
            throw new UpdateException("修改角色状态失败，该角色关联其他用户");
        }
        Integer i = roleMapper.updateRole(role);
        if (i <= 0) {
            throw new UpdateException("修改角色状态失败");
        }
    }

    @Override
    public void deleteRole(ArrDto arrDto) {
        Integer[] ids = arrDto.getId();
        if (hasRoleAssociation(ids)) {
            throw new UpdateException("删除角色失败，该角色关联其他用户");
        }
        Integer i = roleMapper.deleteRole(ids);

        if (i <= 0) {
            throw new UpdateException("删除角色失败");
        }
    }

    @Override
    public void updateRole(RoleEditOrAddDto roleEditOrAddDto) {
        Integer roleId = roleEditOrAddDto.getId();
        Set<Integer> permissions = roleEditOrAddDto.getPermissions();
        Set<Integer> routes = roleEditOrAddDto.getRoutes();
        Role role = MappingUtil.mapObject(roleEditOrAddDto, Role.class);
        roleMapper.updateRole(role);

        // 更新权限列表
        Integer deleteRow1 = roleMapper.deleteRolePermission(roleId);
        if (deleteRow1 <= 0) {
            throw new DeleteException("删除权限列表失败！");
        }
        if (!permissions.isEmpty()){
            Integer insertRow1 = roleMapper.insertRolePermission(roleId, permissions);
            if (insertRow1 <= 0) {
                throw new InsertException("插入权限列表失败！");
            }
        }

        // 更新菜单列表
        Integer deleteRow2 = roleMapper.deleteRoleRoute(roleId);
        if (deleteRow2 <= 0) {
            throw new DeleteException("删除菜单路由列表失败！");
        }
        if (!routes.isEmpty()){
            Integer insertRow2 = roleMapper.insertRoleRoute(roleId, routes);
            if (insertRow2 <= 0) {
                throw new InsertException("插入菜单路由列表失败！");
            }
        }
    }

    @Override
    public void addRole(RoleEditOrAddDto roleEditOrAddDto) {
        Set<Integer> permissions = roleEditOrAddDto.getPermissions();
        Set<Integer> routes = roleEditOrAddDto.getRoutes();
        Role role = MappingUtil.mapObject(roleEditOrAddDto, Role.class);

        Integer insertRow = roleMapper.insert(role);
        Integer insertRoleId = role.getId();

        if (insertRow <= 0){
            throw new InsertException("插入角色失败！");
        }

        // 添加权限
        if (!permissions.isEmpty()){
            Integer insertRow1 = roleMapper.insertRolePermission(insertRoleId, permissions);
            if (insertRow1 <= 0) {
                throw new InsertException("插入权限列表失败！");
            }
        }

        // 添加路由
        if (!routes.isEmpty()){
            Integer insertRow2 = roleMapper.insertRoleRoute(insertRoleId, routes);
            if (insertRow2 <= 0) {
                throw new InsertException("插入菜单路由列表失败！");
            }
        }

    }

    private boolean hasRoleAssociation(Integer[] ids) {
        for (Integer id : ids) {
            List<User> userList = userMapper.findUserByRoleId(id);
            return !userList.isEmpty();
        }
        return false;
    }
}
